package com.adams.audit.operate.service.impl;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONObject;

import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.adams.access.db.bean.AuditRecord;
import com.adams.access.db.bean.AuditStep;
import com.adams.audit.common.AuditConstants;
import com.adams.audit.engine.IAuditService;
import com.adams.audit.operate.dao.IAuditRecordDao;
import com.adams.audit.operate.service.IAuditRecordService;
import com.adams.audit.web.bean.AuditType;
import com.adams.utils.Page;
import com.adams.utils.SpringBeanFactoryUtil;
import com.adams.utils.SystemConfig;

/**
 * 
 * 
 * File Name : AuditRecordServiceImpl.java
 * 
 * @Description : 审核service实现
 */
@Service("auditRecordService")
@Transactional
public class AuditRecordServiceImpl implements IAuditRecordService
{

	private static Log log = LogFactory.getLog(AuditRecordServiceImpl.class);

	@Autowired
	private IAuditRecordDao auditRecordDao;

	@Override
	public AuditRecord getByKey(Long id)
	{
		return auditRecordDao.get(id);
	}

	@Override
	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	public List<AuditType> getAllType()
	{
		List<AuditType> list = new ArrayList<AuditType>();
		String[] typeCodes = AuditConstants.TYPE_CODES;
		String[] typeNames = AuditConstants.TYPE_NAMES;
		String[] typeServices = AuditConstants.TYPE_SERVICES;
		AuditType auditType;
		for (int i = 0; i < typeCodes.length && i < typeNames.length
				&& i < typeServices.length; i++)
		{
			auditType = new AuditType(typeCodes[i], typeNames[i],
					typeServices[i]);
			list.add(auditType);
		}
		return list;
	}

	@Override
	@Transactional
	public void agree(AuditRecord auditRecord) throws Exception
	{
		auditRecord.setAuditStatus(AuditConstants.AUDIT_PASS);
		auditRecord.setEndDate(DateFormatUtils.format(new Date(),
				"yyyy-MM-dd HH:mm:ss"));
		try
		{
			audit(auditRecord);
		} catch (Exception e)
		{
			auditRecord.setId(null);
			throw e;
		}
		// CacheUtil.clearCacheByClass(AuditRecord.class);
	}

	@Override
	@Transactional
	public void refuse(AuditRecord auditRecord) throws Exception
	{
		auditRecord.setAuditStatus(AuditConstants.AUDIT_OVERRULE);
		auditRecord.setEndDate(DateFormatUtils.format(new Date(),
				"yyyy-MM-dd HH:mm:ss"));
		try
		{
			audit(auditRecord);
		} catch (Exception e)
		{
			auditRecord.setId(null);
			throw e;
		}
		// CacheUtil.clearCacheByClass(AuditRecord.class);
	}

	@Override
	@Transactional
	public boolean sendAuditNotice(String contentId, String typeCode,
			String title, int operType, String viewUrl, int UserId)
	{
		AuditRecord auditRecord = new AuditRecord();
		auditRecord.setContentId(contentId);
		auditRecord.setTypeCode(typeCode);
		auditRecord.setTitle(title);
		auditRecord.setOperType(operType);
		auditRecord.setStartDate(DateFormatUtils.format(new Date(),
				"yyyy-MM-dd HH:mm:ss"));
		auditRecord.setViewUrl(viewUrl);
		auditRecord.setSourceType(AuditConstants.INNER_SOURCE_TYPE);
		auditRecord.setAuditStatus(AuditConstants.AUDIT_WAIT);
		auditRecord.setStep(1);
		auditRecord.setSend_user_id(UserId);

		AuditStep auditStep = this.getFirstStepByType(typeCode);
		if (auditStep == null)
			return false;
		auditRecord.setUserId(auditStep.getUser().getId().toString());
		auditRecord.setUserName(auditStep.getUser().getUserName());
		auditRecordDao.save(auditRecord);
		// CacheUtil.clearCacheByClass(AuditRecord.class);
		return true;
	}

	@Override
	@Transactional
	public boolean sendAuditNotice(String contentId, String typeCode,
			String title, int operType, String viewUrl, String callbackUrl,
			int userID)
	{
		AuditRecord auditRecord = new AuditRecord();
		auditRecord.setContentId(contentId);
		auditRecord.setTypeCode(typeCode);
		auditRecord.setTitle(title);
		auditRecord.setOperType(operType);
		auditRecord.setStartDate(DateFormatUtils.format(new Date(),
				"yyyy-MM-dd HH:mm:ss"));
		auditRecord.setViewUrl(viewUrl);
		auditRecord.setCallbackUrl(callbackUrl);
		auditRecord.setSourceType(AuditConstants.OUTER_SOURCE_TYPE);
		auditRecord.setAuditStatus(AuditConstants.AUDIT_WAIT);
		auditRecord.setStep(1);

		AuditStep auditStep = this.getFirstStepByType(typeCode);
		if (auditStep == null)
			return false;
		auditRecord.setSend_user_id(userID);
		auditRecord.setUserId(auditStep.getUser().getId().toString());
		auditRecord.setUserName(auditStep.getUser().getUserName());
		auditRecordDao.save(auditRecord);
		// CacheUtil.clearCacheByClass(AuditRecord.class);
		return true;
	}

	/**
	 * 
	 * Description : 根据ID找到审核记录
	 * 
	 * @return
	 * 
	 */
	public AuditRecord findAuditRecordByID(Long ID)
	{
		return auditRecordDao.get(ID);
	}

	@Override
	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	public Page<AuditRecord> listAuditRecord(String userId, String auditStatus,
			int pageNo, int pageSize)
	{
		String hql = "from AuditRecord where userId='" + userId
				+ "' and auditStatus='" + auditStatus + "'";

		Page<AuditRecord> page = new Page<AuditRecord>();
		page.setPageSize(pageSize);
		page.setCurrentPage(pageNo);
		return auditRecordDao.queryPage(page, hql);
		// return dao.querySplitPage(hql, pageNo, pageSize);
	}

	@Override
	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	public Page<AuditRecord> listHasAuditRecord(String userId, int pageNo,
			int pageSize)
	{
		String hql = "from AuditRecord where userId='" + userId
				+ "' and auditStatus!='" + AuditConstants.AUDIT_WAIT + "'";
		Page<AuditRecord> page = new Page<AuditRecord>();
		page.setPageSize(pageSize);
		page.setCurrentPage(pageNo);
		return auditRecordDao.queryPage(page, hql);
		// return dao.querySplitPage(hql, pageNo, pageSize);
	}

	@Override
	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	public Page<AuditRecord> viewAuditFlow(String contentId, String typeCode,
			int pageNo, int pageSize)
	{
		String hql = "from AuditRecord where contentId='" + contentId
				+ "' and typeCode='" + typeCode + "'";
		Page<AuditRecord> page = new Page<AuditRecord>();
		page.setPageSize(pageSize);
		page.setCurrentPage(pageNo);
		return auditRecordDao.queryPage(page, hql);
		// return dao.querySplitPage(hql, pageNo, pageSize);
	}

	@Override
	@Transactional
	public void deleteRecord(String contentId, String typeCode)
	{
		auditRecordDao.executeUpdate(
				"delete from AuditRecord where contentId=? and typeCode=?",
				contentId, typeCode);
	}

	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	private AuditStep getFirstStepByType(String typeCode)
	{
		String hql = "from AuditStep where typeCode='" + typeCode
				+ "' and step=1";
		List<AuditStep> list = null;
		list = auditRecordDao.query(hql);
		return !(list == null || list.isEmpty()) ? list.get(0) : null;
	}

	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	private AuditStep nextAuditStep(int step, String typeCode)
	{
		String hql = "from AuditStep where typeCode='" + typeCode
				+ "' and step=" + (step + 1);
		List<AuditStep> list = null;
		list = auditRecordDao.query(hql);
		return !(list == null || list.isEmpty()) ? list.get(0) : null;
	}

	private IAuditService getSubAuditService(String typeCode)
	{
		String[] typeCodes = AuditConstants.TYPE_CODES;
		String[] typeServices = AuditConstants.TYPE_SERVICES;
		for (int i = 0; i < typeCodes.length && i < typeServices.length; i++)
		{
			if (typeCode.equals(typeCodes[i]))
			{
				return (IAuditService) SpringBeanFactoryUtil
						.getBean(typeServices[i]);
			}
		}
		return null;
	}

	private void audit(AuditRecord auditRecord) throws Exception
	{
		// 驳回，修改数据状态
		if (AuditConstants.AUDIT_OVERRULE.equals(auditRecord.getAuditStatus()))
		{
			// 判断资源类型
			if (!AuditConstants.OUTER_SOURCE_TYPE.equals(auditRecord
					.getSourceType()))
			{
				// 内部资源回调处理
				IAuditService auditService = getSubAuditService(auditRecord
						.getTypeCode());
				if (auditService != null)
					auditService.modifyContentAuditStatus(auditRecord
							.getContentId(), auditRecord.getTypeCode(),
							auditRecord.getAuditStatus(), auditRecord
									.getOperType());
			} else
			{
				// 外部资源数据回传处理
				String backUrl = auditRecord.getCallbackUrl()
						+ AuditConstants.AUDIT_OVERRULE + "&idea="
						+ auditRecord.getIdea();
				callbackOuterSystem(backUrl);
			}
		}
		// 通过，判断是否有下一审
		else if (AuditConstants.AUDIT_PASS.equals(auditRecord.getAuditStatus()))
		{
			AuditStep nextStep = nextAuditStep(auditRecord.getStep(),
					auditRecord.getTypeCode());
			// 终审，修改数据状态
			if (nextStep == null)
			{
				// 判断资源类型
				if (!AuditConstants.OUTER_SOURCE_TYPE.equals(auditRecord
						.getSourceType()))
				{
					// 内部资源回调处理
					IAuditService auditService = getSubAuditService(auditRecord
							.getTypeCode());
					if (auditService != null)
						auditService.modifyContentAuditStatus(auditRecord
								.getContentId(), auditRecord.getTypeCode(),
								auditRecord.getAuditStatus(), auditRecord
										.getOperType());
				} else
				{
					// 外部资源数据回传处理
					String backUrl = auditRecord.getCallbackUrl()
							+ AuditConstants.AUDIT_PASS + "&idea="
							+ auditRecord.getIdea();
					callbackOuterSystem(backUrl);
				}
			}
			// 创建下一审
			else
			{
				AuditRecord nextRecord = new AuditRecord();
				nextRecord.setContentId(auditRecord.getContentId());
				nextRecord.setTitle(auditRecord.getTitle());
				nextRecord.setTypeCode(auditRecord.getTypeCode());
				nextRecord.setStartDate(DateFormatUtils.format(new Date(),
						"yyyy-MM-dd HH:mm:ss"));
				nextRecord.setAuditStatus(AuditConstants.AUDIT_WAIT);
				nextRecord.setOperType(auditRecord.getOperType());
				nextRecord.setViewUrl(auditRecord.getViewUrl());
				nextRecord.setCallbackUrl(auditRecord.getCallbackUrl());
				nextRecord.setSourceType(auditRecord.getSourceType());
				nextRecord.setStep(auditRecord.getStep() + 1);
				nextRecord.setUserId(nextStep.getUser().getId().toString());
				nextRecord.setUserName(nextStep.getUser().getUserName());
				nextRecord.setSend_user_id(auditRecord.getSend_user_id());
				auditRecordDao.save(nextRecord);
			}
		}
		auditRecordDao.update(auditRecord);
	}

	@SuppressWarnings("unchecked")
	private void callbackOuterSystem(String callbackUrl) throws Exception
	{
		if (callbackUrl.length() > 4
				&& !"http".equalsIgnoreCase(callbackUrl.substring(0, 4)))
		{
			callbackUrl = SystemConfig.getInstance().getString(
					"localhostAddress")
					+ (callbackUrl.startsWith("/") ? callbackUrl
							: ("/" + callbackUrl));
		}

		BufferedInputStream reader = null;
		InputStream in = null;
		StringBuffer sb = new StringBuffer();
		try
		{
			HttpURLConnection urlConnection = (HttpURLConnection) new URL(
					callbackUrl).openConnection();
			urlConnection.setRequestMethod("GET");
			urlConnection.setDoOutput(true);
			urlConnection.setDoInput(true);
			urlConnection.setUseCaches(false);
			in = urlConnection.getInputStream();
			reader = new BufferedInputStream(in);
			byte[] b = new byte[1024 * 5];
			int len;
			while ((len = reader.read(b)) != -1)
			{
				sb.append(new String(b, 0, len, "UTF-8"));
			}
			JSONObject jsonObject = JSONObject.fromObject(sb.toString());
			Map resultMap = (Map) JSONObject.toBean(jsonObject, HashMap.class);
			if (!"200".equals(resultMap.get("resultCode")))
				throw new Exception("资源回调请求修改状态失败！");
		} catch (Exception e)
		{
			log.error(e);
			throw new Exception("资源回调地址访问出错！", e);
		} finally
		{
			try
			{
				if (reader != null)
					reader.close();
				if (in != null)
					in.close();
			} catch (Exception e)
			{
				log.error(e);
			}
		}
	}

	public IAuditRecordDao getAuditRecordDao()
	{
		return auditRecordDao;
	}

	public void setAuditRecordDao(IAuditRecordDao auditRecordDao)
	{
		this.auditRecordDao = auditRecordDao;
	}

}
